from django.shortcuts import render
from rest_framework.decorators import api_view, permission_classes, authentication_classes
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework_simplejwt.authentication import JWTAuthentication
from .models import Movie, MovieClean
import requests
import time
import base64
import random
import hashlib
import json
from django.db import models
from django.db.models import Avg
from django.utils import timezone
import datetime
from django.core.cache import cache
import threading
from bs4 import BeautifulSoup
from django.db import connection


def fetch_movie_data(order_type='0', uuid='192766208a2c8-0286a30a18f6a4-4c657b58-168000-192766208a2c8'):
    """爬取猫眼电影数据"""
    url = "https://piaofang.maoyan.com/dashboard-ajax/movie"
    
    # 更新User-Agent
    user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0'
    
    headers = {
        "User-Agent": user_agent,
        'Cookie': '_lxsdk_cuid=1948ce6c81bc8-090731d3fa23cf-26011851-168000-1948ce6c81bc8; _lxsdk=1948ce6c81bc8-090731d3fa23cf-26011851-168000-1948ce6c81bc8; _lxsdk_s=19563b86a7e-b4a-991-014%7C%7C1',
        "Referer": "https://piaofang.maoyan.com/dashboard/movie?movieId=1491310",
        "Accept": "application/json, text/plain, */*",
        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
        "Connection": "keep-alive",
        "Host": "piaofang.maoyan.com",
        "Sec-Ch-Ua": '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"',
        "Sec-Ch-Ua-Mobile": "?0",
        "Sec-Ch-Ua-Platform": '"Windows"',
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Site": "same-origin"
    }

    try:
        # 生成时间戳和随机索引
        timestamp = str(int(time.time() * 1000))
        index = str(int(random.random() * 10000) + 1)
        
        # Base64编码User-Agent
        user_agent_bytes = user_agent.encode('utf-8')
        User_Agent = base64.b64encode(user_agent_bytes).decode('utf-8')
        
        # 构建签名内容
        sign_params = {
            'method': 'GET',
            'timeStamp': timestamp,
            'index': index,
            'userAgent': User_Agent,
            'channelId': '40009',
            'sVersion': '2',
            'signKey': '1388e4f3c1fdd2850eb193d1e44b2f79'
        }
        
        # 按参数名称排序并构建签名字符串
        sign_str = '&'.join([f"{k}={v}" for k, v in sorted(sign_params.items())])
        sign = hashlib.md5(sign_str.encode('utf-8')).hexdigest()
        
        # 构建请求参数
        params = {
            'orderType': order_type,
            'uuid': uuid,
            'timeStamp': timestamp,
            'User-Agent': User_Agent,
            'index': index,
            'channelId': '40009',
            'sVersion': '2',
            'signKey': sign
        }

        print("\n=== 开始发送请求 ===")
        print(f"URL: {url}")
        print(f"Headers: {json.dumps(headers, indent=2)}")
        print(f"Params: {json.dumps(params, indent=2)}")
        print(f"Sign String: {sign_str}")
        print(f"Sign: {sign}")
        
        # 增加重试机制
        for retry in range(3):
            try:
                response = requests.get(
                    url,
                    params=params,
                    headers=headers,
                    timeout=10,
                    verify=False  # 忽略SSL证书验证
                )
                print(f"\n=== 第{retry + 1}次尝试响应 ===")
                print(f"状态码: {response.status_code}")
                print(f"响应URL: {response.url}")
                
                if response.status_code == 200:
                    try:
                        data = response.json()
                        print(f"响应数据: {json.dumps(data, indent=2)}")
                        
                        if not isinstance(data, dict):
                            raise Exception("响应数据格式错误")
                            
                        if 'movieList' not in data or 'list' not in data['movieList']:
                            raise Exception("响应数据缺少必要字段")
                            
                        data_list = data['movieList']['list']
                        if not data_list:  # 确保列表不为空
                            raise Exception("未获取到电影数据")
                            
                        print(f"成功获取到 {len(data_list)} 条电影数据")
                        movie_office_list = []
                        
                        for i, movie in enumerate(data_list, start=1):
                            try:
                                movie_info = movie.get('movieInfo', {})
                                movie_name = movie_info.get('movieName')
                                
                                if not movie_name:
                                    print(f"跳过无效数据: {movie}")
                                    continue
                                    
                                movie_data = {
                                    'id': i,
                                    'movie_name': movie_name,
                                    'release_info': movie_info.get('releaseInfo', '未知'),
                                    'split_box_rate': movie.get('splitBoxRate', '0%'),
                                    'show_count': int(float(movie.get('showCount', 0))),
                                    'show_count_rate': movie.get('showCountRate', '0%'),
                                    'sum_box_desc': movie.get('sumBoxDesc', '0')
                                }
                                
                                movie_office_list.append(movie_data)
                                print(f"处理电影数据: {movie_name}")
                                
                            except Exception as e:
                                print(f"处理单条数据出错: {str(e)}")
                                continue
                                
                        if not movie_office_list:
                            raise Exception("未能成功处理任何电影数据")
                            
                        return movie_office_list
                        
                    except json.JSONDecodeError as e:
                        print(f"JSON解析错误: {str(e)}")
                        print(f"原始响应: {response.text}")
                        if retry == 2:  # 最后一次重试
                            raise Exception(f"JSON解析失败: {str(e)}")
                else:
                    print(f"请求失败，状态码: {response.status_code}")
                    print(f"响应内容: {response.text}")
                    if retry == 2:  # 最后一次重试
                        raise Exception(f"请求失败，状态码: {response.status_code}")
                        
            except requests.RequestException as e:
                print(f"请求异常: {str(e)}")
                if retry == 2:  # 最后一次重试
                    raise Exception(f"请求异常: {str(e)}")
                    
            # 重试前等待
            if retry < 2:
                time.sleep(2)
                
    except Exception as e:
        print(f"爬取数据失败: {str(e)}")
        raise Exception(f"爬取数据失败: {str(e)}")


@api_view(['GET'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAuthenticated])
def get_movies(request):
    """获取电影列表"""
    try:
        movies = Movie.objects.all().order_by('-updated_at')
        data = [{
            'id': movie.id,
            'movie_name': movie.movie_name,
            'release_info': movie.release_info,
            'split_box_rate': movie.split_box_rate,
            'show_count': movie.show_count,
            'show_count_rate': movie.show_count_rate,
            'sum_box_desc': movie.sum_box_desc,
            'updated_at': movie.updated_at
        } for movie in movies]
        
        return Response({
            'status': 'success',
            'data': data
        })
    except Exception as e:
        return Response({
            'status': 'error',
            'message': str(e)
        }, status=status.HTTP_400_BAD_REQUEST)


@api_view(['POST'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAuthenticated])
def fetch_and_save_movies(request):
    """爬取并保存电影数据到数据库"""
    try:
        # 爬取数据
        movie_list = fetch_movie_data()
        if not movie_list:
            return Response({
                'status': 'error',
                'message': '获取数据失败，请查看服务器日志了解详细信息'
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        try:
            # 清空旧数据
            Movie.objects.all().delete()

            # 保存新数据到数据库
            for movie in movie_list:
                Movie.objects.create(
                    movie_name=movie['movie_name'],
                    release_info=movie['release_info'],
                    split_box_rate=movie['split_box_rate'],
                    show_count=movie['show_count'],
                    show_count_rate=movie['show_count_rate'],
                    sum_box_desc=movie['sum_box_desc'],
                    updated_at=timezone.now()  # 手动设置更新时间
                )

            return Response({
                'status': 'success',
                'message': '数据已成功保存到数据库',
                'count': len(movie_list)
            })
        except Exception as db_error:
            return Response({
                'status': 'error',
                'message': f'数据库操作失败: {str(db_error)}'
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    except Exception as e:
        return Response({
            'status': 'error',
            'message': f'爬取数据失败: {str(e)}'
        }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


@api_view(['GET'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAuthenticated])
def get_movie_stats(request):
    """获取电影统计数据"""
    try:
        # 获取所有电影
        movies = Movie.objects.all()
        
        # 计算总电影数
        total_movies = movies.count()
        
        # 计算今日采集数
        today = timezone.now().date()
        today_movies = movies.filter(
            updated_at__date=today
        ).count()
        
        # 计算总票房（取最大值）
        max_box = 0
        total_box = '￥0'
        
        for movie in movies:
            if not movie.sum_box_desc or movie.sum_box_desc == 'NaN':
                continue
                
            # 移除￥符号和其他非数字字符
            box_str = movie.sum_box_desc.replace('￥', '').strip()
            
            # 如果包含"亿"，转换为"万"
            if '亿' in box_str:
                try:
                    value = float(box_str.replace('亿', '')) * 10000
                    if value > max_box:
                        max_box = value
                        total_box = movie.sum_box_desc
                except ValueError:
                    continue
            else:
                try:
                    value = float(box_str.replace('万', ''))
                    if value > max_box:
                        max_box = value
                        total_box = movie.sum_box_desc
                except ValueError:
                    continue
        
        data = {
            'overview': {
                'total_movies': {
                    'value': total_movies,
                    'unit': '部'
                },
                'today_movies': {
                    'value': today_movies,
                    'unit': '部'
                },
                'total_box': {
                    'value': total_box,
                    'unit': '元'
                }
            },
            'last_updated': timezone.now().strftime('%Y-%m-%d %H:%M:%S')
        }
        
        return Response({
            'status': 'success',
            'data': data
        })
    except Exception as e:
        return Response({
            'status': 'error',
            'message': str(e)
        }, status=status.HTTP_400_BAD_REQUEST)


# 爬虫状态变量
crawler_status = {
    'is_running': False,
    'count': 0,
    'latest_movie': None
}

def crawl_movies():
    """爬虫线程函数"""
    global crawler_status
    try:
        print("\n=== 开始数据采集 ===")
        movie_list = fetch_movie_data()
        
        if movie_list:
            try:
                # 清空旧数据
                Movie.objects.all().delete()
                
                # 保存新数据
                for movie in movie_list:
                    Movie.objects.create(
                        movie_name=movie['movie_name'],
                        release_info=movie['release_info'],
                        split_box_rate=movie['split_box_rate'],
                        show_count=movie['show_count'],
                        show_count_rate=movie['show_count_rate'],
                        sum_box_desc=movie['sum_box_desc'],
                        updated_at=timezone.now()  # 手动设置更新时间
                    )
                    crawler_status['count'] = Movie.objects.count()
                    crawler_status['latest_movie'] = movie['movie_name']
                    
                print(f"成功保存 {len(movie_list)} 条电影数据")
                
                # 数据采集成功后自动停止
                crawler_status['is_running'] = False
                print("数据采集完成，爬虫已自动停止")
                
            except Exception as e:
                error_msg = f"保存数据时出错: {str(e)}"
                print(error_msg)
                crawler_status['is_running'] = False
                crawler_status['error'] = error_msg
                raise Exception(error_msg)
        else:
            error_msg = "未获取到数据"
            print(error_msg)
            crawler_status['is_running'] = False
            crawler_status['error'] = error_msg
            raise Exception(error_msg)
            
    except Exception as e:
        error_msg = f"爬虫线程出错: {str(e)}"
        print(error_msg)
        crawler_status['is_running'] = False
        crawler_status['error'] = error_msg
        raise Exception(error_msg)

@api_view(['POST'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAuthenticated])
def start_crawler(request):
    """启动爬虫"""
    global crawler_status
    
    try:
        if crawler_status['is_running']:
            return Response({
                'status': 'error',
                'message': '爬虫已在运行中'
            })
            
        # 重置爬虫状态
        crawler_status['is_running'] = True
        crawler_status['count'] = 0
        crawler_status['latest_movie'] = None
        crawler_status['error'] = None
        
        # 启动爬虫线程
        thread = threading.Thread(target=crawl_movies)
        thread.daemon = True
        thread.start()
        
        # 等待一小段时间，确保线程已启动
        time.sleep(1)
        
        # 检查是否有启动错误
        if crawler_status.get('error'):
            raise Exception(crawler_status['error'])
            
        return Response({
            'status': 'success',
            'message': '爬虫已启动',
            'data': {
                'is_running': crawler_status['is_running'],
                'count': crawler_status['count'],
                'latest_movie': crawler_status['latest_movie'],
                'error': crawler_status['error']
            }
        })
    except Exception as e:
        error_msg = f'启动爬虫失败: {str(e)}'
        crawler_status['is_running'] = False
        crawler_status['error'] = error_msg
        return Response({
            'status': 'error',
            'message': error_msg
        })

@api_view(['POST'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAuthenticated])
def stop_crawler(request):
    """停止爬虫"""
    global crawler_status
    
    if not crawler_status['is_running']:
        return Response({
            'status': 'error',
            'message': '爬虫未在运行'
        })
        
    crawler_status['is_running'] = False
    
    return Response({
        'status': 'success',
        'message': '爬虫已停止'
    })

@api_view(['GET'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAuthenticated])
def get_crawler_status(request):
    """获取爬虫状态"""
    global crawler_status
    try:
        return Response({
            'status': 'success',
            'data': {
                'is_running': crawler_status.get('is_running', False),
                'count': crawler_status.get('count', 0),
                'latest_movie': crawler_status.get('latest_movie'),
                'error': crawler_status.get('error')
            }
        })
    except Exception as e:
        error_msg = f'获取爬虫状态失败: {str(e)}'
        return Response({
            'status': 'error',
            'message': error_msg
        })

@api_view(['GET'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAuthenticated])
def fetch_movies_api(request):
    """手动获取电影数据的API端点"""
    try:
        movie_list = fetch_movie_data()
        if not movie_list:
            return Response({
                'status': 'error',
                'message': '获取数据失败'
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            
        # 清空旧数据
        Movie.objects.all().delete()
        
        # 保存新数据
        for movie in movie_list:
            Movie.objects.create(
                movie_name=movie['movie_name'],
                release_info=movie['release_info'],
                split_box_rate=movie['split_box_rate'],
                show_count=movie['show_count'],
                show_count_rate=movie['show_count_rate'],
                sum_box_desc=movie['sum_box_desc'],
                updated_at=timezone.now()  # 手动设置更新时间
            )
            
        return Response({
            'status': 'success',
            'message': '数据已更新',
            'count': len(movie_list)
        })
        
    except Exception as e:
        return Response({
            'status': 'error',
            'message': str(e)
        }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

# 数据清洗状态
cleaner_status = {
    'is_cleaning': False,
    'processed_count': 0,
    'error_count': 0,
    'message': None
}

@api_view(['POST'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAuthenticated])
def start_clean(request):
    """启动数据清洗"""
    global cleaner_status
    
    try:
        if cleaner_status['is_cleaning']:
            return Response({
                'status': 'error',
                'message': '清洗任务已在运行中'
            })
            
        # 重置清洗状态
        cleaner_status['is_cleaning'] = True
        cleaner_status['processed_count'] = 0
        cleaner_status['error_count'] = 0
        cleaner_status['message'] = None
        
        # 启动清洗线程
        thread = threading.Thread(target=clean_movies)
        thread.daemon = True
        thread.start()
        
        # 等待一小段时间，确保线程已启动
        time.sleep(1)
        
        # 检查是否有启动错误
        if cleaner_status.get('error'):
            raise Exception(cleaner_status['error'])
        
        return Response({
            'status': 'success',
            'message': '清洗任务已启动',
            'data': cleaner_status
        })
    except Exception as e:
        error_msg = f'启动清洗失败: {str(e)}'
        cleaner_status['is_cleaning'] = False
        cleaner_status['error'] = error_msg
        return Response({
            'status': 'error',
            'message': error_msg
        })

@api_view(['POST'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAuthenticated])
def reset_clean(request):
    """重置清洗状态"""
    global cleaner_status
    
    try:
        if cleaner_status['is_cleaning']:
            return Response({
                'status': 'error',
                'message': '清洗任务运行中，无法重置'
            })
            
        # 重置所有状态
        cleaner_status['is_cleaning'] = False
        cleaner_status['processed_count'] = 0
        cleaner_status['error_count'] = 0
        cleaner_status['message'] = None
        cleaner_status['error'] = None
        
        try:
            # 清空清洗表中的旧数据并重置自增ID
            with connection.cursor() as cursor:
                cursor.execute("TRUNCATE TABLE movie_clean;")  # 这会同时清空数据并重置 AUTO_INCREMENT
            
            return Response({
                'status': 'success',
                'message': '清洗状态已重置',
                'data': cleaner_status
            })
        except Exception as db_error:
            print(f"数据库重置失败: {str(db_error)}")
            return Response({
                'status': 'error',
                'message': f'数据库重置失败: {str(db_error)}'
            })
            
    except Exception as e:
        error_msg = f'重置清洗状态失败: {str(e)}'
        print(error_msg)
        return Response({
            'status': 'error',
            'message': error_msg
        })

@api_view(['GET'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAuthenticated])
def get_clean_status(request):
    """获取清洗状态"""
    global cleaner_status
    
    try:
        return Response({
            'status': 'success',
            'data': {
                'is_cleaning': cleaner_status.get('is_cleaning', False),
                'processed_count': cleaner_status.get('processed_count', 0),
                'error_count': cleaner_status.get('error_count', 0),
                'message': cleaner_status.get('message'),
                'error': cleaner_status.get('error')
            }
        })
    except Exception as e:
        error_msg = f'获取清洗状态失败: {str(e)}'
        return Response({
            'status': 'error',
            'message': error_msg
        })

def clean_movies():
    """电影数据清洗函数"""
    global cleaner_status
    try:
        movies = Movie.objects.all().order_by('id')  # 按ID排序
        total_count = movies.count()
        
        print(f"开始清洗数据，共 {total_count} 条记录")
        cleaner_status['total_count'] = total_count
        processed_count = 0  # 本地计数器
        error_count = 0      # 本地计数器
        
        # 清空清洗表中的旧数据并重置自增ID
        with connection.cursor() as cursor:
            cursor.execute("TRUNCATE TABLE movie_clean;")  # 这会同时清空数据并重置 AUTO_INCREMENT
        
        def clean_percentage(value):
            """清洗百分比数据"""
            if not value:
                return '0%'
            # 移除所有空白字符
            value = ''.join(value.split())
            # 处理 <1% 的情况
            if '<' in value:
                return '0.1%'  # 将 <1% 转换为 0.1%
            # 移除百分号并尝试转换为浮点数
            try:
                number = float(value.replace('%', ''))
                return f"{number:.1f}%"
            except ValueError:
                return '0%'
        
        for i, movie in enumerate(movies, start=1):
            try:
                # 基础数据处理
                clean_data = {
                    'movie_name': movie.movie_name,
                    'release_info': movie.release_info or '未知',  # 处理发行信息缺失
                    'split_box_rate': movie.split_box_rate,
                    'show_count': movie.show_count,
                    'show_count_rate': movie.show_count_rate,
                    'sum_box_desc': movie.sum_box_desc or '0'  # 处理票房缺失
                }
                
                has_error = False  # 用于标记当前记录是否有错误
                
                # 清洗票房数据
                if clean_data['sum_box_desc']:
                    # 移除￥符号和其他非数字字符
                    box_str = clean_data['sum_box_desc'].replace('￥', '').strip()
                    
                    # 统一转换为"万"为单位
                    if '亿' in box_str:
                        try:
                            value = float(box_str.replace('亿', '')) * 10000
                            clean_data['sum_box_desc'] = f"{value:.2f}万"
                        except ValueError:
                            clean_data['sum_box_desc'] = '0万'
                            has_error = True
                    elif '万' in box_str:
                        try:
                            value = float(box_str.replace('万', ''))
                            clean_data['sum_box_desc'] = f"{value:.2f}万"
                        except ValueError:
                            clean_data['sum_box_desc'] = '0万'
                            has_error = True
                    else:
                        clean_data['sum_box_desc'] = '0万'
                else:
                    clean_data['sum_box_desc'] = '0万'
                
                # 清洗场次数据
                if isinstance(clean_data['show_count'], str):
                    try:
                        clean_data['show_count'] = int(float(clean_data['show_count']))
                    except ValueError:
                        clean_data['show_count'] = 0
                        has_error = True
                elif clean_data['show_count'] is None:
                    clean_data['show_count'] = 0
                    has_error = True
                
                # 清洗占比数据
                clean_data['split_box_rate'] = clean_percentage(clean_data['split_box_rate'])
                clean_data['show_count_rate'] = clean_percentage(clean_data['show_count_rate'])
                
                # 保存清洗后的数据到新表
                MovieClean.objects.create(
                    **clean_data,
                    updated_at=timezone.now()  # 手动设置更新时间
                )
                
                processed_count += 1
                if has_error:
                    error_count += 1
                
                # 更新状态
                cleaner_status['processed_count'] = processed_count
                cleaner_status['error_count'] = error_count
                cleaner_status['message'] = f"正在处理: {movie.movie_name} ({processed_count}/{total_count})"
                
            except Exception as e:
                error_msg = f"清洗电影数据时出错: {str(e)}"
                print(error_msg)
                error_count += 1
                cleaner_status['error_count'] = error_count
                cleaner_status['error'] = error_msg
                continue
                
        print(f"数据清洗完成，共处理 {processed_count} 条数据，错误 {error_count} 条")
        cleaner_status['message'] = f"数据清洗完成，共处理 {processed_count} 条数据，错误 {error_count} 条"
        cleaner_status['processed_count'] = processed_count
        cleaner_status['error_count'] = error_count
        
    except Exception as e:
        error_msg = f"清洗过程出错: {str(e)}"
        print(error_msg)
        cleaner_status['error'] = error_msg
        raise Exception(error_msg)
    finally:
        cleaner_status['is_cleaning'] = False

@api_view(['GET'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAuthenticated])
def get_cleaned_movies(request):
    """获取清洗后的电影列表"""
    try:
        movies = MovieClean.objects.all().order_by('-id')
        data = [{
            'id': movie.id,
            'movie_name': movie.movie_name,
            'release_info': movie.release_info,
            'split_box_rate': movie.split_box_rate,
            'show_count': movie.show_count,
            'show_count_rate': movie.show_count_rate,
            'sum_box_desc': movie.sum_box_desc,
            'updated_at': movie.updated_at
        } for movie in movies]
        
        return Response({
            'status': 'success',
            'data': data
        })
    except Exception as e:
        return Response({
            'status': 'error',
            'message': str(e)
        }, status=status.HTTP_400_BAD_REQUEST)
